﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Audio;

using SmlEngine.Sprites.Collections;
using SmlEngine.Graphics;
using SmlEngine.Extensions;
using SmlEngine.Sprites.Base.WorldMap;
using SmlEngine.Sprites.Base;

namespace SmlEngine.Sprites
{
    public class WorldPlayer : Sprite, IWorldDrawable
    {
        #region Properties and Fields
        private const float MaxSpeed = 2.0f; // 2 pixels per frame for now
        public Direction dir = Direction.Bottom;
        private int frameLength;
        private WorldPlayerPoses poses;

        private SoundEffect mapStop;

        World owner;

        private long score;
        public long Score
        {
            get { return score; }
            set
            {
                if (value < 0) value = 0;
                score = value;
            }
        }

        private int lives;
        public int Lives
        {
            get { return lives; }
            set
            {
                if (value < 1) value = 1;
                lives = value;
            }
        }

        private int coins;
        public int Coins
        {
            get { return coins; }
            set
            {
                if (value < 0) value = 0;
                if (value > 100) value = 100;
                coins = value;
            }
        }

        public Vector2 tilePosition
        {
            get
            {
                return new Vector2((float)Math.Floor(Position.X / 16), (float)Math.Floor(Position.Y / 16));
            }
        }

        private int frameCounter = 0;
        private int currentFrame = 0;

        private List<List<Rectangle>> sourceRects = new List<List<Rectangle>>();
        private string sourceName;
        private int framesPerPose;

        #region Moving
        public bool IsMoving = false;
        private Vector2 moveDistance;
        private Vector2 destination
        {
            get
            {
                return Position + moveDistance;
            }
        }
        private Vector2 speed;

        public Rectangle Area
        {
            get
            {
                return new Rectangle((int)Position.X, (int)Position.Y, Size.X, Size.Y);
            }
        }

        public Direction LastMoveDirection
        {
            get;
            private set;
        }

        #region Speed Constants
        private readonly Vector2 upSpeed = new Vector2(0, -2);
        private readonly Vector2 downSpeed = new Vector2(0, 2);
        private readonly Vector2 rightSpeed = new Vector2(2, 0);
        private readonly Vector2 leftSpeed = new Vector2(-2, 0);
        #endregion

        #endregion

        public List<Texture2D> gfxOnePoseFrames = new List<Texture2D>();
        public List<Texture2D> gfxUpFrames = new List<Texture2D>();
        public List<Texture2D> gfxDownFrames = new List<Texture2D>();
        public List<Texture2D> gfxLeftFrames = new List<Texture2D>();
        public List<Texture2D> gfxRightFrames = new List<Texture2D>();
        #endregion

        #region Methods
        public WorldPlayer(ContentManager cm, string content_path, WorldPlayerPoses Poses, List<List<Vector2>> framePositions, Vector2 size, int frame_Length, GraphicsDevice gd, Vector2 startLocation, long cScore, int cLives, int cCoins)
        {
            poses = Poses;
            framesPerPose = framePositions[0].Count;
            sourceName = content_path;
            int i = 0;
            foreach (List<Vector2> lv in framePositions)
            {
                sourceRects.Add(new List<Rectangle>());
                foreach (Vector2 v in lv)
                {
                    sourceRects[i].Add(v.ToRectangle(size));
                }
                i++;
            }
            Position = startLocation;
            Score = cScore;
            Lives = cLives;
            Coins = cCoins;
            frameLength = frame_Length;

            // temporary
            mapStop = cm.Load<SoundEffect>(@"sound_test\map");
        }

        public void Move(Vector2 distance)
        {
            if (distance != Vector2.Zero)
            {
                IsMoving = true;
                moveDistance = distance;

                if (moveDistance.X > 0)
                { 
                    speed = rightSpeed;
                    LastMoveDirection = Direction.Right;
                }
                else if (moveDistance.X < 0) 
                { 
                    speed = leftSpeed;
                    LastMoveDirection = Direction.Left;
                }
                else if (moveDistance.Y < 0) 
                {
                    speed = upSpeed;
                    LastMoveDirection = Direction.Top;
                }
                else if (moveDistance.Y > 0) 
                {
                    speed = downSpeed;
                    LastMoveDirection = Direction.Bottom;
                }
            }
        }

        public void ResetMove()
        {
            IsMoving = false;
            speed = Vector2.Zero;
            moveDistance = Vector2.Zero;
            mapStop.Play();
        }

        public override void Update(GameTime gameTime)
        {
            if (IsMoving)
            {
                Vector2 projectedPosition = Position + speed;
                if (speed.Equals(upSpeed))
                {
                    if (projectedPosition.Y < destination.Y)
                    {
                        Position = destination;
                        ResetMove();
                    }
                    else Position += speed; moveDistance -= speed;
                }
                else if (speed.Equals(downSpeed))
                {
                    if (projectedPosition.Y > destination.Y)
                    {
                        Position = destination;
                        ResetMove();
                    }
                    else Position += speed; moveDistance -= speed;
                }
                else if (speed.Equals(rightSpeed))
                {
                    if (projectedPosition.X > destination.X)
                    {
                        Position = destination;
                        ResetMove();
                    }
                    else Position += speed; moveDistance -= speed;
                }
                else if (speed.Equals(leftSpeed))
                {
                    if (projectedPosition.X < destination.X)
                    {
                        Position = destination;
                        ResetMove();
                    }
                    else Position += speed; moveDistance -= speed;
                }
            }
//            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            if (poses == WorldPlayerPoses.FourPoses)
            {
                if (IsMoving)
                {
                    if (frameCounter == frameLength)
                    {
                        frameCounter = 0;
                        if (currentFrame == gfxUpFrames.Count - 1) currentFrame = 0; else currentFrame++;
                    }
                    else frameCounter++;
                }
                else { currentFrame = 0; }

                switch (dir)
                {
                    case Direction.Top:
                        spriteBatch.Draw(gfxUpFrames[currentFrame], Position, Color.White);
                        break;
                    case Direction.Bottom:
                        spriteBatch.Draw(gfxDownFrames[currentFrame], Position, Color.White);
                        break;
                    case Direction.Left:
                        spriteBatch.Draw(gfxLeftFrames[currentFrame], Position, Color.White);
                        break;
                    case Direction.Right:
                        spriteBatch.Draw(gfxRightFrames[currentFrame], Position, Color.White);
                        break;
                }
            }
            else
            {
                if (frameCounter == frameLength)
                {
                    frameCounter = 0;
                    if (currentFrame == gfxOnePoseFrames.Count - 1) currentFrame = 0; else currentFrame++;
                }
                else frameCounter++;

                spriteBatch.Draw(gfxOnePoseFrames[currentFrame], Position, Color.White);
            }
        }
        #endregion

        public void LoadContent(GraphicsDevice gd, ContentManager cm)
        {
            Texture2D playerTiles = null;
            bool isContentPath = GraphicsHelper.IsContentPath(sourceName);

            if (isContentPath)
            {
                playerTiles = cm.Load<Texture2D>(sourceName);
            }
            else
            {
                playerTiles = playerTiles.LoadFromFile(sourceName, ref gd);
            }

            if (poses == WorldPlayerPoses.OnePose)
            {
                foreach (Rectangle r in sourceRects[0])
                {
                    if (isContentPath) gfxOnePoseFrames.Add(SpritesheetManager.GetTile(sourceName, r, cm));
                    else gfxOnePoseFrames.Add(new Texture2D(gd, r.Width, r.Height).LoadFromFile(sourceName, ref gd));
                }
            }
            else
            {
                // Up frames
                foreach (Rectangle r in sourceRects[0])
                {
                    if (isContentPath) gfxUpFrames.Add(SpritesheetManager.GetTile(sourceName, r, cm));
                    else gfxUpFrames.Add(new Texture2D(gd, r.Width, r.Height).LoadFromFile(sourceName, ref gd));
                }

                // Down frames
                foreach (Rectangle r in sourceRects[1])
                {
                    if (isContentPath) gfxDownFrames.Add(SpritesheetManager.GetTile(sourceName, r, cm));
                    else gfxDownFrames.Add(new Texture2D(gd, r.Width, r.Height).LoadFromFile(sourceName, ref gd));
                }
                // Left frames
                foreach (Rectangle r in sourceRects[2])
                {
                    if (isContentPath) gfxLeftFrames.Add(SpritesheetManager.GetTile(sourceName, r, cm));
                    else gfxLeftFrames.Add(new Texture2D(gd, r.Width, r.Height).LoadFromFile(sourceName, ref gd));
                }
                // Right frames
                foreach (Rectangle r in sourceRects[3])
                {
                    if (isContentPath) gfxRightFrames.Add(SpritesheetManager.GetTile(sourceName, r, cm));
                    else gfxRightFrames.Add(new Texture2D(gd, r.Width, r.Height).LoadFromFile(sourceName, ref gd));
                }
            }
        }

        public void UnloadContent()
        {
            gfxOnePoseFrames.Clear();
            gfxUpFrames.Clear();
            gfxLeftFrames.Clear();
            gfxRightFrames.Clear();
            gfxDownFrames.Clear();
        }

        public Direction InvertDirection(Direction direction)
        {
            switch (direction)
            {
                case Direction.Left:
                    return Direction.Right;
                case Direction.Right:
                    return Direction.Left;
                case Direction.Top:
                    return Direction.Bottom;
                case Direction.Bottom:
                    return Direction.Top;
            }
            throw new ArgumentException("You should never see this message show up, this is just to placate VS.");
        }
    }
}
